// rk_vendor_storage.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "getopt.h"
#include "iinmap.h"
#include <windows.h>
#include "RKUpgradeDll.h"
#include "IDAtteConfigs.h"
#include <string>
#define nof(x) (sizeof(x) / sizeof(x[0]))
#define DEBUG(x) printf x

void print_usage(char *exe_nane)
{
    fprintf(stderr, "Usage: %s command(v1.0) [-r/-w/-q] -i userid[/name] -t types -v value\n", exe_nane);
    fprintf(stdout, "       item info can user -q option: userid:00; vendor id:01; name:SN");


}

int ReverseFindStrA(const char * bRecieveBytes,int iBytesLength, const char* CharStrToFind, int CharStrLength)
{
    BOOL bResult = TRUE;

    if (0 >= CharStrLength) {
        return -1;
    }
    for (int i = iBytesLength - CharStrLength ; i>= 0; i-- ) {
        bResult = TRUE;
        for (int j = 0; j < CharStrLength; j++) {
            if (bRecieveBytes[i + j] != CharStrToFind[j] ) {
                bResult = FALSE;
                break;
            }
        }
        if ( bResult ) {
            return i;
        }
    }
    return -1;
}
/* Bit formats */
enum CMD_OP {
    CMD_NULL = 0,
    CMD_TEST,
    CMD_READ,
    CMD_WRITE
};

enum ITEM_TYPES {
    CMD_NONE = 0,
    CMD_BIN,
    CMD_STR,
    CMD_MAC,
};

enum ITEM_TYPES parse_types(char *str)
{
    if (strcmp(str, "string") == 0) {
        DEBUG(("value type is string\r\n"));
        return CMD_STR;
    } else if (strcmp(str, "binary") == 0) {
        DEBUG(("value type is binary\r\n"));
        return CMD_BIN;
    } else if (strcmp(str, "mac") == 0) {
        DEBUG(("value type is mac\r\n"));
        return CMD_MAC;
    }
    return CMD_NONE;
}

int tchar_to_byte(const char ch)
{
    int result = 0;

    if(ch >= '0' && ch <= '9') {
        result = (int)(ch - '0');
    } else if(ch >= 'a' && ch <= 'z' ){
        result = (int)(ch - 'a') + 10;
    } else if(ch >= 'A' && ch <= 'Z') {
        result = (int)(ch - 'A') + 10;
    } else{
        result = -1;
    }
    return result;
}

#define LINE_LENGTH 16
void printhex(const char *data, int len, char *sep)
{
    int i = 0, j = 0;
    int lenmo = len - 1;
    if(!sep)
        sep = " ";
    printf("hex result:\n");
    if (0 == len)
        printf("\n");
    for (i = 0; i < lenmo; ) {
        for (j = 0; ((i + j) < lenmo)  && (j < LINE_LENGTH); j ++) {
            if (j != LINE_LENGTH - 1){
                printf("%02x%s", data[i + j], sep);
            } else {
                printf("%02x\n", data[i + j]);
            }
        }
        i += j;
    }
    printf("%02x\n", data[i]);
}

int hex_string_convert(const char *str, char *data, unsigned int len)
{
    int i, strsize, outsize, datasize;

    datasize = len;
    strsize = strlen(str);
    outsize = strsize/2;
    if (outsize >= datasize)
        outsize = datasize;

    for(i = 0; i < outsize; i ++) {
        data[i] = (tchar_to_byte(str[2*i]) << 4) | (tchar_to_byte(str[2*i + 1]));
    }
    return outsize;
}

int parse_string(enum ITEM_TYPES item_type, const char *argv_str, char **data)
{
    char *out = 0;
    unsigned int olen;

    /* input string NULL, or length is zero */
    if (!argv_str || argv_str[0] == '\0')
        return -1;
    if(CMD_MAC == item_type) {
        olen = 6;
        out = (char *)malloc(olen);
        if (!out) {
            DEBUG(("%s:malloc fail\n", __FUNCTION__));
            return -12;
        }
        if(olen != hex_string_convert(argv_str, out, olen)) {
            DEBUG(("convert MAC fail\n"));
            goto parse_exit;
        } else {
            printhex(out, 6, ":");
        }
    } else if (CMD_BIN == item_type) {
        olen = strlen(argv_str)/2;
        out = (char *)malloc(olen);
        if (!out) {
            DEBUG(("%s:malloc fail\n", __FUNCTION__));
            return -12;
        }
        if(olen != hex_string_convert(argv_str, out, olen)) {
            DEBUG(("convert binary fail\n"));
            goto parse_exit;
        } else {
            printhex(out, olen, " ");
        }
    } else if (CMD_STR == item_type) {
        olen = strlen(argv_str);
        out = (char *)malloc(olen);
        if (!out) {
            DEBUG(("%s:malloc fail\n", __FUNCTION__));
            return -12;
        }
        memcpy(out, argv_str, olen);
        printhex(out, olen, " ");
    }
    *data = out;
    return olen;
parse_exit:
    if (out) {
        free(out);
    }
    return -1;
}

int parse_data(enum ITEM_TYPES item_type, const char *data, int len, const char **str)
{
    int i;
    char *out = 0;
    unsigned int olen;

    if(CMD_MAC == item_type) {
        if (len != 6) {
            DEBUG(("error mac format\n"));
            return -22;
        }
        olen = 18; /* strlen("0A:0B:11:22:33:44") + 1*/
        out = (char *)malloc(olen);
        if (!out){
            DEBUG(("%s:malloc fail\n", __FUNCTION__));
            return -12;
        }
        sprintf(out, "%02X:%02X:%02X:%02X:%02X:%02X", data[0], data[1], data[2], data[3], data[4], data[5]);
    } else if (CMD_BIN == item_type) {
        olen = len*2 + 1;
        out = (char *)malloc(olen);
        if (!out) {
            DEBUG(("%s:malloc fail\n", __FUNCTION__));
            return -12;
        }
        for (i = 0; i < len; i++) {
            sprintf(&out[i*2], "%02X", data[i]);
        }
    } else if (CMD_STR == item_type) {
        olen = len + 1;
        out = (char *)malloc(olen);
        if (!out){
            DEBUG(("%s:malloc fail\n", __FUNCTION__));
            return -12;
        }
        memcpy(out, data, len);
        out[len] = '\0';
    }
    *str = out;
    return olen;
parse_exit:
    if (out) {
        free(out);
    }
    return -1;
}

unsigned char *ToBuffer(AttestationIds *IDAtte, int id)
{
    switch(id) {
    case ATTESTATION_ID_BRAND:
        return IDAtte->brand;
    case ATTESTATION_ID_DEVICE:
        return IDAtte->device;
    case ATTESTATION_ID_PRODUCT:
        return IDAtte->product;
    case ATTESTATION_ID_SERIAL:
        return IDAtte->serial;
    case ATTESTATION_ID_IMEI:
        return IDAtte->imei;
    case ATTESTATION_ID_IMEI2:
        return IDAtte->second_imei;
    case ATTESTATION_ID_MEID:
        return IDAtte->meid;
    case ATTESTATION_ID_MANUFACTURER:
        return IDAtte->manufacturer;
    case ATTESTATION_ID_MODEL:
        return IDAtte->model;
    }
    return NULL;
}

bool parse_atte(const char* input, AttestationIds *atteid)
{
    char *deli = ";";
    std::string s = input;
    std::string subs;
    int idx;
    int len;
    std::string::size_type  beg = 0, end;
    int count;

    len = s.length();
    for (idx = ATTESTATION_ID_BRAND; idx < ATTESTATION_ID_COUNT && beg < len; ) {
        end = s.find_first_of(deli, beg);
        if (std::string::npos != end && end > beg) {
            subs = s.substr(beg, end - beg);
            beg = end + 1;
            strcpy_s((char *)ToBuffer(atteid, idx++), PROP_LEN, subs.c_str());
        } else if (std::string::npos != end && end == beg) {
            beg = end + 1;
            memset((char *)ToBuffer(atteid, idx++), 0, PROP_LEN);
        } else if (std::string::npos == end && std::string::npos != beg) {
            subs = s.substr(beg);
            strcpy_s((char *)ToBuffer(atteid, idx++), PROP_LEN, subs.c_str());
            break;
        }
    }
    return true;
}

int do_vendor(TCHAR *m_szLogPath, CMD_OP op,  IINMAP *iimap, ITEM_TYPES item_type, const TCHAR *value)
{
    INIT_DEV_INFO InitDevInfo;
    INIT_LOG_INFO InitLogInfo;
    INIT_CALLBACK_INFO InitCallbackInfo;
    PSTRUCT_DEVICE_DESC pDevs = NULL;
    UINT nDeviceCount = 0;
    BOOL m_bUpgradeDllInitOK;
    BOOL bRet;
    char *data = NULL;
    unsigned short len;
    int id = iimap->vendorid;

    /* UpgradeDll init */
    InitDevInfo.bScan4FsUsb = FALSE;
    InitDevInfo.emSupportDevice = 0;
    InitDevInfo.uiRockMscTimeout = 30;
    InitDevInfo.uiRockusbTimeout = 30;
    InitDevInfo.usRockMscPid = 0;
    InitDevInfo.usRockMscVid = 0;
    InitDevInfo.usRockusbPid = 0;
    InitDevInfo.usRockusbVid = 0;
    if (strlen(m_szLogPath)) {
        InitLogInfo.bLogEnable = TRUE;
        InitLogInfo.lpszLogPathName = m_szLogPath;
    } else {
        InitLogInfo.bLogEnable = FALSE;
        InitLogInfo.lpszLogPathName = NULL;
    }
    InitCallbackInfo.pProgressPromptProc = NULL;//you can set it to ProgressPromptProc for showing upgrade info;
    InitCallbackInfo.pUpgradeStepPromptProc = NULL;//you can set it to UpgradeStepPromptProc for showing progress info;
    m_bUpgradeDllInitOK = RK_Initialize(InitDevInfo, InitLogInfo, InitCallbackInfo);
    if (!m_bUpgradeDllInitOK) {
        printf(_T("Initialize RKUpgrade dll failed!\r\n"));
        return -1;
    }

    nDeviceCount = RK_ScanDevice(&pDevs);
    if(nDeviceCount <= 0) {
        printf("No Devices Found!!!\r\n");
        goto exit;
    } else {
        printf("Found %d device, the 1st one is:\r\n%s\r\n", nDeviceCount, pDevs[0].szLinkName);
    }

    if (op == CMD_WRITE) {
        if(iimap->index == ITEM_ID_ATTE) {
            int len = sizeof(AttestationIds) + 8; /* 4 bytes tag + 4 length */
            AttestationIds *pIDAtte;

            data = (char *)malloc(len);
            memset(data, 0, len);
            sprintf((char *)data, "%s", IDATTE_TAG);
            data[4] = sizeof(AttestationIds) & 0xff;
            data[5] = (sizeof(AttestationIds) >> 8) & 0xff;
            pIDAtte = (AttestationIds *)(data + 8);
            parse_atte(value, pIDAtte);
            bRet = RK_WriteVendorRpmbData(iimap->vendorid, 1, (PBYTE)data, len);
        } else {
            len = parse_string(item_type, value, &data);
            if (len < 0) {
                DEBUG(("parsing input data fail\n"));
                goto exit;
            } 
            bRet = RK_WriteVendorRpmbData(id, 0, (PBYTE)data, len);
            free(data);
        }
    } else {
        const char *rstr = NULL;
        if (!data) {
            len = 1024;
            data = (char *)malloc(len);
            memset(data, 0, sizeof(data[0])*len);
        }
        bRet = RK_ReadVendorRpmbData(id, 0, (PBYTE)data, len);
        if (bRet) {
            DEBUG(("read %d from vendor with size of %d\r\n", id, len));
            if (parse_data(item_type, data, len, &rstr) > 0) {
                printf("%s\n", rstr);
                free((void *)rstr);
            } else {
                DEBUG(("parse read data fail\n"));
                printhex(data, len, " ");
            }
        }
        free((void *)data);
    }
    if (bRet)
        printf("%s %d successful\r\n", op == CMD_READ ? "read from" : "write to",  id);
    else 
        printf("%s %d fail\r\n", op == CMD_READ ? "read from" : "write to",  id);
exit:
    RK_Uninitialize();
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int opt;
    int ret = -1;
    int query = 0;
    IINMAP *iinmap = NULL;
    enum CMD_OP op = CMD_READ;
    enum ITEM_TYPES item_type = CMD_STR;
    TCHAR *value = NULL;
    TCHAR m_szModulePath[MAX_PATH] = {0};
    TCHAR m_szLogPath[MAX_PATH] = {0};
    TCHAR appname[MAX_PATH] = {0};
    DWORD dwRet;

    if (argc > 0)
        sprintf_s(appname, nof(appname), "%s", argv[0]);
    if (argc == 1) {
        print_usage(appname);
        return -2;
    }
    GetModuleFileName(NULL, m_szModulePath, MAX_PATH);
    if (strlen(m_szModulePath)) {
        int pos = ReverseFindStrA(m_szModulePath, strlen(m_szModulePath), "\\", strlen("\\"));
        if (pos >= 0 && pos < strlen(m_szModulePath)) {
            sprintf_s(m_szLogPath, nof(m_szLogPath), "%s", m_szModulePath);
            m_szLogPath[pos] = '\0';
            strcat(m_szLogPath, "\\log");
        }
    }
    if (!strlen(m_szLogPath)) {
        sprintf_s(m_szLogPath, nof(m_szLogPath), "D:\\log");
    }

    dwRet = GetFileAttributes(m_szLogPath);
    if (INVALID_FILE_ATTRIBUTES != dwRet && 0 != (dwRet & FILE_ATTRIBUTE_DIRECTORY)) {
    } else {
        CreateDirectory(m_szLogPath, NULL);
    }

    while ((opt = getopt(argc, argv, "qrwt:f:i:v:")) != -1) {
        switch (opt) {
        case 'r':
            op = CMD_READ;
            break;
        case 'w':
            op = CMD_WRITE;
            break;
        case 'i':
        case 'I': {
            int len = strlen(optarg);
            if (len == strspn(optarg, "0123456789")) {
                iinmap = GetIINMAP(atoi(optarg), -1, NULL);
            }  else {
                iinmap = GetIINMAP(-1, -1, optarg);
            }
            break;
        }
        case 't':
            item_type = parse_types(optarg);
            break;
        case 'v':
            value = optarg;
            break;
        case 'q':
            query = 1;
            break;
        default: /* '?' */
            print_usage(appname);
            exit(-1);
        }
    }
#if _DEBUG
#if 1
    item_type = CMD_STR;
    op == CMD_READ;
    id = 100;
#else
    item_type = CMD_STR;
    value = "12233";
    op == CMD_WRITE;
    id = 100;
#endif
#endif
    if (query) {
        printf("item info: \r\n");
        print_iinmap_info();
        return 0;
    }
    if (!iinmap) {
        printf("item must specifyed with -i\r\n");
        print_usage(appname);
        return -2;
    }
    if (op == CMD_WRITE && !value) {
        printf("Value must specifyed with -v\r\n");
        print_usage(appname);
        return -2;

    }

    printf("%s %s\r\n", op == CMD_READ ? "read from" : "write to",  iinmap->name);
    do_vendor(m_szLogPath, op, iinmap, item_type, value);
    return 0;
}
